社交web允许用户之间相互联系。
例如:
关注者、好友、联系人、联络人或伙伴。
记录两个用户之间的定向联系,在数据库查询中也要使用这种联系。
一、论数据库关系
一对多关系
数据库使用关系建立记录之间的联系。其中,一对多关系是最常用的关系类型,它把一个记录和一组相关的记录联系在一起。实现这种关系,要在“多”这一层加一个外键,指向“一”这一侧,例如下列代码:
class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer,primary_key = True) #... users = db.relationship('User', backref='role', lazy='dynamic')class User(UserMixin, db.Model): __tablename__ = 'users' #... role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
其中,users的数据库表中添加了一个外键role_id 指向了roles表的id
大部分的其他关系类型都可以从一对多类型中衍生。
多对一关系从“多”这一侧看,就是一对多关系。一对一关系类型是简化版的一对多关
多对多关系
解决方法是添加第三张表,这个表称为关联表。多对多关系可以分解成原表和关联表之间的两个一对多关系。
用SQLAlchemy实现图中的关系。
registrations = db.Table('registrations', db.Column('student_id', db.Integer, db.ForeignKey('students.id')),db.Column('class_id', db.Integer, db.ForeignKey('classes.id')))class Student(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) classes = db.relationship('Class', secondary=registrations, backref=db.backref('students', lazy='dynamic'), lazy='dynamic')class Class(db.Model): id = db.Column(db.Integer, primary_key = True) name = db.Column(db.String)
多对多关系仍使用定义一对多关系的db.relationship() 方法进行定义,但在多对多关系中,必须把secondary 参数设为关联表。多对多关系可以在任何一个类中定义,backref 参数会处理好关系的另一侧。关联表就是一个简单的表,不是模型,SQLAlchemy 会自动接管这个表。
自引用关系
如果关系中的两侧都在同一个表中,这种关系称为自引用关系。
高级多对多关系
使用多对多关系时,往往需要存储所联两个实体之间的额外信息。对用户之间的关注来说,可以存储用户关注另一个用户的日期,这样就能按照时间顺序列出所有关注者。这种
信息只能存储在关联表中,但是在之前实现的学生和课程之间的关系中,关联表完全是由SQLAlchemy 掌控的内部表。为了能在关系中处理自定义的数据,我们必须提升关联表的地位,使其变成程序可访问的模型。
app/models/user.py:关注关联表的模型实现
class Follow(db.Model): __tablename__ = 'follows' follower_id = db.Column(db.Integer,db.ForeignKey('users.id'),primary_key = True) followed_id = db.Column(db.Integer,db.ForegnKey('users.id'),primary_key = True) timestamp = db.Column(db.DateTime,default = datetime.utcnow)
app/models/user.py:使用两个一对多关系实现的多对多关系
class User(UserMixin, db.Model):# ... followed = db.relationship('Follow', foreign_keys=[Follow.follower_id], backref=db.backref('follower', lazy='joined'), lazy='dynamic', cascade='all, delete-orphan') followers = db.relationship('Follow', foreign_keys=[Follow.followed_id], backref=db.backref('followed', lazy='joined'), lazy='dynamic', cascade='all, delete-orphan')
程序现在要处理两个一对多关系,以便实现多对多关系。由于这些操作经常需要重复执行,所以最好在User 模型中为所有可能的操作定义辅助方法
app/models.py:关注关系的辅助方法
#关注关系的辅助方法 def follow(self,user): if not self.is_following(user): f = Follow(follower=self,followed=user) db.session.add(f) def unfollow(self,user): f = self.followed.filter_by(followed_id = user.id).first() if f: db.session.delete(f) def is_following(self,user): return self.followed.filter_by(follow_id = user.id).first() is not None def is_followed(self,user): return self.followers.filter_by(follow_id = user.id).first() is not None
二、在资料页中显示关注者
app/templates/user.html:在用户资料页上部添加关注信息
{% extends "base.html" %}{% import "_macros.html" as macros %}{% block title %}微博 - { { user.username }}{% endblock %}{% block page_content %}{ { user.username }}
{% if user.name or user.location %}{% if user.name %}{
{ user.name }}{% endif %} {% if user.location %} From { { user.location }} {% endif %} {% endif %} {% if current_user.is_administrator() %} {% endif %} {% if user.about_me %}{
{ user.about_me }}{% endif %}创建时间 {
{ moment(user.member_since).format('L') }}. 最后访问 { { moment(user.last_seen).fromNow() }}.{
{ user.posts.count() }} 博客文章.{% if current_user.can(Permission.FOLLOW) and user != current_user %} {% if not current_user.is_following(user) %} 关注 {% else %} 取消关注 {% endif %} {% endif %} 关注者: { { user.followers.count() }} 被关注: { { user.followed.count() }} {% if current_user.is_authenticated and user != current_user and user.is_following(current_user) %} | 关注了你 {% endif %}
{% if user == current_user %} 编辑个人资料 {% endif %} {% if current_user.is_administrator() %} 编辑个人资料 [管理员] {% endif %}
Posts by { { user.username }}
{% include '_posts.html' %}{% if pagination %}{ { macros.pagination_widget(pagination, '.user', username=user.username) }}{% endif %}{% endblock %}